home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
t3_1
/
doc.lha
/
documentation
/
manual
/
control.mss
< prev
next >
Wrap
Text File
|
1987-06-30
|
18KB
|
494 lines
@part[CONTROL, Root "TMAN.MSS"] @Comment{-*-System:TMAN-*-}
@chap[Control]
@dc{ Talk about calls, tail-recursion, conditionals, etc. }
@section [Conditionals]
@label[ConditionalsSection] @Comment{ref: objects chapter}
@index[Conditionals]
@info[NOTES="Special form"]
@desc[@el[](COND . @i[clauses]) @yl[] @i[value-of-clause]]
@tc[COND] is a general conditional construct. Each @i[clause] should
have one of the following forms:
@begin[tc]
@begin[itemize]
(@i[test] . @i[body])
(@i[test])
(@i[test] => @i[procedure])
@end[Itemize]
@end[tc]
The @i[test] expressions are evaluated in sequence until one yields true,
at which point:
@Itemize[
If it is part of a clause of the form @wt[(@i[test] . @i[body])],
then @i[body], an implicit block, is evaluated, and its value is what
the @tc[COND]-expression yields.
If the clause has the form @wt[(@i[test])], then the @tc[COND]-expression yields
the value of @i[test] (which of course is non-null).
If the clause has the form
@wt[(@i[test] => @i[procedure])], then the @i[procedure]
expression is evaluated, and its value, which should be
a procedure, is called with the value of the @i[test] as its one and only
argument. The @tc[COND] yields the value of that call.
]
Example:
@begin[TEG]
(COND ((PAIR? X) (GAUR (CAR X)))
((FIXNUM? X) (GAUR X)))
@end[TEG]
If all the @i[tests] yield false, then the @tc[COND] yields an
undefined value.
See section @ref[undefined semantics section] for a discussion of undefined
values.
The system variable @tix[ELSE] has a non-null value. This provides a
convenient way to specify a branch to take when all other @i[tests]
fail.
@begin[TEG]
(COND ((PAIR? X) (GAUR (CAR X)))
((ASSQ X *SAMPLE-ALIST*) => CDR)
((GAUR X))
(ELSE NIL))
@end[TEG]
@dc{ Important: devise examples which illustrate all the various
features of @tc[COND]. }
@EndDesc[COND]
@info[NOTES="Special form"]
@desc[(XCOND . @i[clauses]) @yl[] @i[value-of-clause]]
Similar to @tc[COND], but its effect is undefined (and presumably an error
is signalled) if no @i[clause] is selected. @tc[X] is mnemonic for
@qu"exhaustive".
@enddesc[XCOND]
@desc[@el[]ELSE @yl[] @i[true]]
@tc[ELSE] has as its value some true value.
It exists mainly for use in @tc[COND].
@EndDesc[ELSE]
@info[NOTES="Special form"]
@descN[
F1="@el[](IF @i[test consequent alternate]) @yl[] @i[value-of-arm]", FN1="IF",
F2="@el[](IF @i[test consequent]) @yl[] @i[undefined]"
]
@tc[IF] is a primitive two-way conditional. The @i[test] expression
is evaluated. If it yields true, then the @i[consequent] expression is
evaluated, and the @tc[IF] yields its value. Otherwise, the
@i[alternate] expression, if any, is evaluated, and the @tc[IF] yields its
value. If @i[test] yields false and there is no @i[alternate],
then the value of the @tc[IF] is undefined.
@begin[ProgramExample]
(IF T 1 2) @ev[] 1
(IF (PAIR? 'X) 'FOO 'BAR) @ev[] BAR
(IF @i[test] @i[consequent]) @ce[] (IF @i[test] @i[consequent] (UNDEFINED-VALUE))
@end[ProgramExample]
@EndDescN[IF]
@AnEquivE[Tfn="CASE",Efn="SELECTQ"]
@info[NOTES="Special form",EQUIV="CASEQ"]
@desc[(CASE @i[key] . @i[clauses]) @yl[] @i[value-of-clause]]
@tc[CASE] performs a multi-way dispatch based on the value of the
@i[key] expression. Each @i[clause] should be of the form
@wt[(@i[keys] . @i[body])], where @i[keys] is a list of values (@i[not]
expressions!) against which the value of the @i[key] expression is
compared (using @tc[EQ?]), and @i[body] is an implicit block. The
body of the clause which matches the @i[key] value is evaluated, and
the @tc[CASE]-expression yields the value of the body. The last
@i[clause] may be of the form @wt[(ELSE . @i[body])]; this designates
a default action to be taken if no other clause is selected. If the
@i[key] never matches and there is no default clause, then the
@tc[CASE]-expression yields an undefined value.
@begin[ProgramExample]
(CASE 'B ((A) 'LOSE) ((B C) 'WIN) (ELSE NIL)) @ev[] WIN
@end[ProgramExample]
@EndDesc[CASE]
@info[NOTES="Special form"]
@desc[(XCASE @i[key] . @i[clauses]) @yl[] @i[value-of-clause]]
Similar to @tc[CASE], but no @tc[ELSE]-clause is permitted, and
the effect is undefined (and presumably an error is signalled)
if no @i[clause] is selected.
@EndDesc[XCASE]
@info[NOTES="Special form"]
@desc[(SELECT @i[key] . @i[clauses]) @yl[] @i[value-of-clause]]
@tc[SELECT] is like @tc[CASE], but the car of each @i[clause] is a list
of expressions that are evaluated, instead of a list of constants.
@begin[ProgramExample]
(DEFINE *RED-COLOR* ...)
(DEFINE *GREEN-COLOR* ...)
(DEFINE *BLUE-COLOR* ...)
(SELECT COLOR
((*RED-COLOR* *BLUE-COLOR*) 'INORGANIC)
((*GREEN-COLOR*) 'ORGANIC))
@end[ProgramExample]
@EndDesc[SELECT]
@info[NOTES="Special form"]
@desc[(XSELECT @i[key] . @i[clauses]) @yl[] @i[value-of-clause]]
Similar to @tc[SELECT], but no @tc[ELSE]-clause is permitted, and
the effect is undefined (and presumably an error is signalled)
if no @i[clause] is selected.
@EndDesc[XSELECT]
@info[NOTES="Type predicate"]
@descN[
F1="(NOT @i[object]) @yl[] @i[boolean]", FN1="NOT",
F2="(FALSE? @i[object]) @yl[] @i[boolean]", FN2="FALSE?"
]
@tc[NOT] returns true if @i[object] is false, and returns false otherwise.
@begin[ProgramExample]
(NOT NIL) @ev[] @r[true]
(NOT T) @ev[] @r[false]
(NOT 3) @ev[] @r[false]
@end[ProgramExample]
@EndDescN[]
@info[NOTES="Special form"]
@desc[@el[](AND . @i[tests]) @yl[] @i[value-of-test] @r[or] @i[false]]
The @i[test] expressions are evaluated from left to right, until one
evaluates to false, at which point the @tc[AND] yields false.
If no @i[test] evaluates to false then the value of the @tc[AND]
is the value of the last @i[test].
@begin[ProgramExample]
(AND 3 4) @ev[] 4
(AND 3 NIL) @ev[] @r[false]
(AND) @ev[] @r[true]
@end[ProgramExample]
@EndDesc[AND]
@info[NOTES="Special form"]
@desc[@el[](OR . @i[tests]) @yl[] @i[value-of-test] @i[or] @i[false]]
The @i[test] expressions are evaluated from left to right, until one
evaluates to true (i.e., not null); its value is yielded as the value
of the @tc[OR]. If no @i[test] evaluates to true, then the value
of the @tc[OR] is false.
@begin[ProgramExample]
(OR 3 4) @ev[] 3
(OR 3 NIL) @ev[] 3
(OR) @ev[] @r[false]
@end[ProgramExample]
@EndDesc[OR]
@desc[(*AND . @i[objects]) @yl[] @i[object]]
If any of the @i[objects] is false, @tc[*AND] returns false; otherwise
it returns the last @i[object]. This is superficially similar to
@tc[AND], but it is a procedure, not a special form. That is, the
value of @tc[*AND] is a procedure, whereas @tc[AND] is a reserved word
which introduces a special syntactic form.
This fact implies that (a) the variable @tc[*AND] has a value suitable
to be passed as an argument to some procedure, and
(b) a call to @tc[*AND] behaves differently from
an @tc[AND] special form in the case that the argument expressions have
side-effects. In
@begin[ProgramExample]
(AND NIL (FOO))
@end[ProgramExample]
@tc[FOO] will not be called, whereas in
@begin[ProgramExample]
(*AND NIL (FOO))
@end[ProgramExample]
@tc[FOO] @i[will] be called.
@EndDesc[*AND]
@desc[(*OR . @i[objects]) @yl[] @i[object]]
If any of the @i[objects] is true, @tc[*OR] returns that object; otherwise
it returns false. @tc[*OR]'s relation to @tc[OR] is
analogous to @tc[*AND]'s relation to @tc[AND].
@EndDesc[*OR]
@desc[(*IF @i[test consequent alternate]) @yl[] @i[object]]
If @i[test] is true, @tc[*IF] returns @i[consequent];
otherwise it returns @i[alternate]. @tc[*IF]'s relation to @tc[IF] is
analogous to @tc[*AND]'s relation to @tc[AND].
@EndDesc[*IF]
@section[Iteration]
@index[Iteration]
@index[Loops]
Separate iteration constructs are not strictly necessary in @Tau[],
since iteration may be written using recursive procedures defined
by @tc[LABELS] (or even @tc[DEFINE]). There is no penalty for this
in @Tau[], either in efficiency or practicality (for example, stack
size), because tail-recursions are implemented without net growth
of stack, and it is expected that compilers will implement local
procedures efficiently.
However, the following two iteration constructs provide a somewhat
more convenient notation for loops than does @tc[LABELS]. (There
are also some higher-order procedures which perform iteration; see
for example @tc[MAP], page @pageref[MAP], and @tc[WALK], page
@pageref[WALK].)
@info[NOTES="Special form"]
@desc[(DO @i[specs] (@i[end-test] . @i[exit-forms]) . @i[body]) @~
@yl[] @i[value-of-exit]]
Iterates until @i[end-test] yields true.
Each @i[spec] should be of the form
@wt[(@i[variable initial next])].
The @i[variables] are initialized (bound) in parallel to the values
of the @i[initial]-expressions, as with @tc[LET];
then, the @i[end-test] and @i[body] are executed iteratively.
If the @i[end-test] yields true on any iteration, then the loop
terminates, at which point the @i[exit-forms] are evaluated.
The value of the @tc[DO]-expression is the value of the
last @i[exit-form], or the value of the @i[end-test]
if there are no @i[exit-forms] (this is like a @tc[COND] clause).
At the end of each iteration, the @i[next]-expressions are all evaluated,
and the @i[variables] are re-bound
to the values of the @i[next]-expressions.
@tc[DO] is useful for loops which have a single termination condition,
such as numerical, ALGOL-style @tc[for]-loops, loops where desired
results are accumulated in variables, and loops that perform
side-effects on every element in a sequence
(see @tc[WALK], page @PageRef[WALK]).
@begin[ProgramExample]
(REVERSE L) @ce[]
(DO ((L L (CDR L))
(RESULT '() (CONS (CAR L) RESULT)))
((NULL? L) RESULT)))
(VECTOR-FILL VECTOR CONSTANT) @ce[]
(DO ((I (-1+ (VECTOR-LENGTH VECTOR)) (-1+ I)))
((<0? I) VECTOR)
(VSET VECTOR I CONSTANT))
@end[ProgramExample]
Any @tc[DO]-expression may be rewritten using the more primitive @tc[LABELS]
construct. The following example has only one induction variable and
one @i[body]-expression, and assumes that there are no name conflicts
with the variable @tc[LOOP].
@begin[ProgramExample]
(DO ((@i[variable] @i[initial] @i[next])) @i[exit-clause] @i[body])
@ce[]
(LABELS (((LOOP @i[variable])
(COND @i[exit-clause]
(ELSE @i[body]
(LOOP @i[next])))))
(LOOP @i[initial]))
@end[ProgramExample]
@EndDesc[DO]
@info[NOTES="Special form"]
@desc[(ITERATE @i[variable specs] . @i[body]) @yl[] @i[value-of-body]]
Another iteration construct.
@i[Specs] has the form
@begin[ProgramExample]
((@i[arg@-[1] val@-[1]]) (@i[arg@-[2] val@-[2]]) ... (@I[arg@-[n] val@-[n]]))
@end[ProgramExample]
The @i[variable] is bound to a local procedure whose formal parameters
are @i[arg@-[1] arg@-[2] ... arg@-[n]]. The procedure is initially invoked
with the @i[val]s bound to the corresponding @i[arg]s.
@tc[ITERATE] is more flexible than @tc[DO]. It is useful in loops where
the terminating condition occurs in the middle of the body of the loop,
or where there is more than one terminating condition, or where the
iteration may occur in several places.
For example, consider the following example (from @Cite[STEELE78REV]),
a procedure to sort a list of trees into atoms and lists:
@begin[ProgramExample]
(DEFINE (COLLATE X)
(ITERATE COL ((Z X) (ATOMS '()) (LISTS '()))
(COND ((NULL? Z)
(LIST ATOMS LISTS))
((ATOM? (CAR Z))
(COL (CDR Z) (CONS (CAR Z) ATOMS) LISTS))
(ELSE
(COL (CDR Z) ATOMS (CONS (CAR Z) LISTS))))))
@end[ProgramExample]
A rough analogy @tc[ITERATE : LABELS :: LET : LAMBDA] holds.
@tc[ITERATE] expands trivially into @tc[LABELS], but may be more convenient
than @tc[LABELS] for the same reason that a @tc[LET] form may be preferable
than a call to a @tc[LAMBDA]-expression.
@begin[ProgramExample]
(ITERATE @i[variable] @~
((@i[arg@-[1] val@-[1]]) (@i[arg@-[2] val@-[2]]) ... (@i[arg@-[n] val@-[n]])) @~
. @i[body])
@ce[]
(LABELS (((@i[variable arg@-[1] arg@-[2] ... arg@-[n]]) . @i[body])) @~
(@i[variable val@-[1] val@-[2] ... val@-[n]]))
@end[ProgramExample]
@EndDesc[ITERATE]
@section[Procedure application]
The only operation which all procedures support is invocation.
All initial system routines, like @tc[CAR], @tc[LIST], and @tc[EQ?], are
procedures. Procedures are usually created by evaluating @tc[LAMBDA]-
or @tc[DEFINE]-forms.
@index[procedures]
The standard way to apply a procedure to arguments is with a
@i[call] expression. Calls are described on page @pageref[callsemantics].
@index[calls]
@dc{ Describe @tc[CALL], just for fun? People might not understand
that it's redundant. }
@info[NOTES="Type predicate"]
@desc[(PROCEDURE? @i[object]) @yl[] @i[boolean]]
Returns true if @i[object] is a procedure, i.e., if it may be called.
@EndDesc[PROCEDURE?]
@desc[(APPLY @i[procedure] . @i[objects]) @yl[] @i[object]]
@tc[APPLY] applies a procedure to a list of arguments.
The sequence of @i[object] arguments should have the form
@begin[ProgramExample]
@i[arg@-[1] arg@-[2]] ... @i[arg@-[n] arglist]
@end[ProgramExample]
where @i[n] may be 0. The @i[procedure] is called with
@i[arg@-[1]], @i[arg@-[2]], etc., as the first @i[n] arguments,
and the members of @i[arglist] as final arguments.
@begin[ProgramExample]
(APPLY CONS '(1 2)) @ev[] (1 . 2)
(APPLY CONS 1 '(2)) @ev[] (1 . 2)
(APPLY CONS 1 2 '()) @ev[] (1 . 2)
(APPLY LIST 1 2 3 '(4 5 6)) @ev[] (1 2 3 4 5 6)
@end[ProgramExample]
@EndDesc[APPLY]
@section[Sequencing]
@info[NOTES="Special form",EQUIV="PROGN",BRIEF="; Like PROGN"]
@desc[@el[](BLOCK . @i[body]) @yl[] @i[value-of-body]]
@i[Body] is a non-empty sequence of expressions.
These expressions are evaluated in order from left to right.
The @tc[BLOCK]-expression
yields the value of the last expression.
Because their values are ignored, expressions other than the last are
useful only for side-effects they may cause.
Since the bodies of most special forms are implicitly blocks, @tc[BLOCK]
is useful only in places where syntactically a single expression is
needed, but some sequencing is required.
@EndDesc[BLOCK]
@info[NOTES="Special form",EQUIV="PROG1",BRIEF="; Like PROG1"]
@desc[(BLOCK0 @i[first-form] . @i[body]) @yl[] @i[value-of-first-form]]
The @i[first-form] is evaluated, then the expressions in the @i[body]
are evaluated. The @tc[BLOCK0]-expression yields @i[first-form]'s value.
@EndDesc[BLOCK0]
@section[Non-local exits]
@AnEquivE[Tfn="CATCH",Efn="RETURN"]
@AnEquivE[Tfn="CATCH",Efn="CATCH"]
@AnEquivE[Tfn="CATCH",Efn="THROW"]
@AnEquivE[Tfn="CATCH",Efn="*CATCH"]
@AnEquivE[Tfn="CATCH",Efn="*THROW"]
@info[NOTES="Special form"]
@desc[(CATCH @i[variable] . @i[body]) @yl[] @i[value-of-body]]
@tc[CATCH] provides a structured, non-local exit facility
similar to @tc[PROG] and @tc[RETURN] (without @tc[GO]),
or @tc[CATCH] and @tc[THROW], in other Lisps.
The @i[variable] is bound to an @i[escape procedure]
of one argument. If the escape procedure is called
as a result of evaluating @i[body], then the argument to the
@i[escape procedure]
is returned as the value of the @tc[CATCH]-expression.
If the escape procedure is not called, then @tc[CATCH] returns whatever
@i[body] returns.
A call to an escape procedure is called a @i[throw].@index[throws]
In the following example, the call to @tc[LIST] never happens,
because one of its subforms performs a throw.
@begin[ProgramExample]
(CATCH X (LIST 1 (X 2) 3)) @ev[] 2
@end[ProgramExample]
@tau[]'s @tc[CATCH] is a restriction of SCHEME's.
Escape procedures are only valid within the dynamic extent of the
@tc[CATCH]-expression; the @tc[CATCH] can yield a value at most once.
Escape procedures should not be returned @qu"upwards."
A throw may cause side-effects if the dynamic state in effect when the
escape procedure is called differs from that in effect when evaluation
of its corresponding @tc[CATCH]-expression began.@index[dynamic state]
In this case the
throw undoes the effects of @tc[BIND] forms, and must perform cleanup
action requested by @tc[UNWIND-PROTECT] forms, in the process of
@qu"unwinding" the evaluation context from that of the throw to that of
the @tc[CATCH]. See section @ref[dynamic state section] for
descriptions of @tc[BIND] and @tc[UNWIND-PROTECT].
@EndDesc[CATCH]
@section[Lazy evaluation]
@iix[Delays] provide a general mechanism for delayed (lazy) evaluation,
also known as @qu"call-by-need".
@index[Lazy evaluation]
@index[Call-by-need]
@dc{ Where does this belong, really? }
@dc{ Should we admit that this is just a trivial macro in terms of
@tc[LAMBDA]? }
@info[NOTES="Special form"]
@desc[(DELAY @i[expression]) @yl[] @i[delay]]
Returns a delay of the @i[expression]. The computation of the expression,
which happens at most once, is delayed until its value is
requested with @tc[FORCE].
@begin[ProgramExample]
(DEFINE (INF-LIST-OF-INTEGERS N)
(CONS N (DELAY (INF-LIST-OF-INTEGERS (1+ N)))))
(HEAD (TAIL (TAIL (INF-LIST-OF-INTEGERS 1)))) => 3
(DEFINE HEAD CAR)
(DEFINE (TAIL OBJ) (FORCE (CDR OBJ)))
@end[ProgramExample]
The behavior of @tc[DELAY] and @tc[FORCE] can be described by the
following hypothetical implementation:
@begin[ProgramExample]
(DEFINE-SYNTAX (DELAY EXP)
`(MAKE-DELAYED-OBJECT (LAMBDA () ,EXP)))
(DEFINE (MAKE-DELAYED-OBJECT THUNK)
(LET ((FORCED-YET? NIL)
(FORCED-VALUE NIL))
(OBJECT NIL
((FORCE SELF)
(COND ((NOT FORCED-YET?)
(SET FORCED-VALUE (THUNK))
(SET FORCED-YET? T)))
FORCED-VALUE))))
(DEFINE-OPERATION (FORCE OBJ)
OBJ)
@end[ProgramExample]
@EndDesc[DELAY]
@Desc[(FORCE @i[delay]) @yl[] @i[object]]
Forces the computation of the @i[delay]'s value.
If @t[FORCE] has not previously been applied to the delay,
then the @i[expression] in the original @tc[DELAY]-expression
which created @i[delay] (see above)
is evaluated, and its value is returned.
If @t[FORCE] has previously been applied to the delay,
then the value previously computed is returned.
If @tc[FORCE] is applied to a non-delay, then it simply returns
its argument.
@EndDesc[FORCE]